/**
 * 序列化工具，适配常用的STL容器类型
 */

#pragma once

#include <vector>
#include <string>
#include <unordered_set>
#include <set>
#include <unordered_map>
#include <map>
#include <queue>
#include <stack>
#include <list>
#include <array>
#include <tuple>


/**
 * 型别特化与偏特化定义
 */
namespace hello
{

// 擦除类型的引用属性
template<typename T> struct rem_ref { using type = T; };
template<typename T> struct rem_ref<T&> { using type = T; };
template<typename T> struct rem_ref<T&&> { using type = T; };
template<typename T>
using rem_ref_t = typename rem_ref<T>::type;

// 擦除类型的const属性
template<typename T> struct rem_const { using type = T; };
template<typename T> struct rem_const<const T> { using type = T; };
template<typename T>
using rem_const_t = typename rem_const<T>::type;

// 擦除类型的引用和const属性
template<typename T>
using rem_const_ref_t = typename rem_const<typename rem_ref<T>::type>::type;


// 内置类型集合
template<typename T> struct is_builtin_type : std::false_type {};
template<> struct is_builtin_type<int> : std::true_type {};
template<> struct is_builtin_type<unsigned int> : std::true_type {};
template<> struct is_builtin_type<unsigned long int> : std::true_type {};
template<> struct is_builtin_type<unsigned long long int> : std::true_type {};
template<> struct is_builtin_type<float> : std::true_type {};
template<> struct is_builtin_type<double> : std::true_type {};
template<> struct is_builtin_type<long double> : std::true_type {};
template<> struct is_builtin_type<short> : std::true_type {};
template<> struct is_builtin_type<long> : std::true_type {};
template<> struct is_builtin_type<long long> : std::true_type {};
template<> struct is_builtin_type<char> : std::true_type {};
template<> struct is_builtin_type<signed char> : std::true_type {};
template<> struct is_builtin_type<unsigned char> : std::true_type {};
template<> struct is_builtin_type<wchar_t> : std::true_type {};
template<> struct is_builtin_type<char16_t> : std::true_type {};
template<> struct is_builtin_type<char32_t> : std::true_type {};
template<> struct is_builtin_type<bool> : std::true_type {};
template<> struct is_builtin_type<void> : std::true_type {};
template<> struct is_builtin_type<nullptr_t> : std::true_type {};
template<typename T> inline constexpr bool is_builtin_type_v = is_builtin_type<T>::value;

// STL vector
template<typename T> struct is_stl_vector : std::false_type {};
template<typename T> struct is_stl_vector<std::vector<T>> : std::true_type{};
template<typename T> inline constexpr bool is_stl_vector_v = is_stl_vector<T>::value;

// STL string
template<typename T> struct is_stl_string : std::false_type {};
template<> struct is_stl_string<std::string> : std::true_type{};
template<typename T> inline constexpr bool is_stl_string_v = is_stl_string<T>::value;

// STL list
template<typename T> struct is_stl_list : std::false_type {};
template<typename T> struct is_stl_list<std::list<T>> : std::true_type{};
template<typename T> inline constexpr bool is_stl_list_v = is_stl_list<T>::value;

// STL array
template<typename T> struct is_stl_array : std::false_type {};
template<typename T, size_t N> struct is_stl_array<std::array<T, N>> : std::true_type {};
template<typename T> inline constexpr bool is_stl_array_v = is_stl_array<T>::value;

// STL unordered_set
template<typename T> struct is_stl_unordered_set : std::false_type {};
template<typename T> struct is_stl_unordered_set<std::unordered_set<T>> : std::true_type{};
template<typename T> inline constexpr bool is_stl_unordered_set_v = is_stl_unordered_set<T>::value;

// STL set
template<typename T> struct is_stl_set : std::false_type {};
template<typename T> struct is_stl_set<std::set<T>> : std::true_type{};
template<typename T> inline constexpr bool is_stl_set_v = is_stl_set<T>::value;

// STL map
template<typename T> struct is_stl_map : std::false_type {};
template<typename K, typename V> struct is_stl_map<std::map<K, V>> : std::true_type {};
template<typename T> inline constexpr bool is_stl_map_v = is_stl_map<T>::value;

// STL unordered_map
template<typename T> struct is_stl_unordered_map : std::false_type {};
template<typename K, typename V> struct is_stl_unordered_map<std::unordered_map<K, V>> : std::true_type {};
template<typename T> inline constexpr bool is_stl_unordered_map_v = is_stl_unordered_map<T>::value;

// STL queue
template<typename T> struct is_stl_queue : std::false_type {};
template<typename T> struct is_stl_queue<std::queue<T>> : std::true_type {};
template<typename T> inline constexpr bool is_stl_queue_v = is_stl_queue<T>::value;

// STL stack
template<typename T> struct is_stl_stack : std::false_type {};
template<typename T> struct is_stl_stack<std::stack<T>> : std::true_type {};
template<typename T> inline constexpr bool is_stl_stack_v = is_stl_stack<T>::value;

// STL tuple
template<typename T> struct is_stl_tuple : std::false_type {};
template<typename... Args> struct is_stl_tuple<std::tuple<Args...>> : std::true_type {};
template<typename T> inline constexpr bool is_stl_tuple_v = is_stl_tuple<T>::value;

// 自定义类型（不属于上述任何类型）
template<typename T>
struct is_custom_type :
    std::integral_constant<bool,
        !is_builtin_type_v<T> &&
        !is_stl_vector_v<T> &&
        !is_stl_string_v<T> &&
        !is_stl_list_v<T> &&
        !is_stl_array_v<T> &&
        !is_stl_unordered_map_v<T> &&
        !is_stl_unordered_set_v<T> &&
        !is_stl_map_v<T> &&
        !is_stl_set_v<T> &&
        !is_stl_queue_v<T> &&
        !is_stl_stack_v<T> &&
        !is_stl_tuple_v<T>
    >
{
};
template<typename T> inline constexpr bool is_custom_type_v = is_custom_type<T>::value;

}


/**
 * 二进制序列化工具
 */
namespace hello
{

/**
 * 二进制序列化工具
 */
class serializer
{
public:
    explicit serializer(std::string& s) : _cont(s) { }

public:
    template<typename... Args>
    inline
    void operator () (Args&&... args)
    {
        archive(std::forward<Args>(args)...);
    }

private:
    template<typename T, typename... Args>
    inline void archive(T&& first, Args&&... rest)
    {
        // 依据不同的类型匹配不同的序列化函数
        using in_type = rem_const_ref_t<T>;
        if constexpr (is_builtin_type_v<in_type>)
            process_serial_builtin(first);
        else if constexpr (is_stl_vector_v<in_type>)
            process_serial_vector(first);
        else if constexpr (is_stl_string_v<in_type>)
            process_serial_string(first);
        else if constexpr (is_stl_list_v<in_type>)
            process_serial_list(first);
        else if constexpr (is_stl_array_v<in_type>)
            process_serial_array(first);
        else if constexpr (is_stl_unordered_map_v<in_type>)
            process_serial_unordered_map(first);
        else if constexpr (is_stl_unordered_set_v<in_type>)
            process_serial_unordered_set(first);
        else if constexpr (is_stl_map_v<in_type>)
            process_serial_map(first);
        else if constexpr (is_stl_set_v<in_type>)
            process_serial_set(first);
        else if constexpr (is_stl_queue_v<in_type>)
            process_serial_queue(first);
        else if constexpr (is_stl_stack_v<in_type>)
            process_serial_stack(first);
        else if constexpr (is_stl_tuple_v<in_type>)
            process_serial_tuple(first);
        else if constexpr (is_custom_type_v<in_type>)
            process_serial_custom(first);
        else ;

        if constexpr (sizeof...(rest) > 0)
        {
            archive(rest...);
        }
    }

    template<typename T>
    inline
    typename std::enable_if_t<is_builtin_type_v<rem_const_ref_t<T>>, void>
    process_serial_builtin(T x)
    {
        _cont.append(reinterpret_cast<char*>(std::addressof(x)), sizeof(x));
    }

    template<typename T>
    inline
    void process_serial_vector(const std::vector<T>& x)
    {
        __iterable_serial(x);
    }

    inline
    void process_serial_string(const std::string& x)
    {
        size_t sz = x.size();
        _cont.append(reinterpret_cast<char*>(std::addressof(sz)), sizeof(sz));
        _cont.append(x.data(), sz);
    }

    template<typename T>
    inline
    void process_serial_list(const std::list<T>& x)
    {
        __iterable_serial(x);
    }

    template<typename T, size_t N>
    inline
    void process_serial_array(const std::array<T, N>& x)
    {
        for (auto& item : x) archive(item);
    }

    template<typename K, typename V>
    inline
    void process_serial_unordered_map(const std::unordered_map<K, V>& x)
    {
        __pair_based_serial(x);
    }

    template<typename T>
    inline
    void process_serial_unordered_set(const std::unordered_set<T>& x)
    {
        __iterable_serial(x);
    }

    template<typename K, typename V>
    inline
    void process_serial_map(const std::map<K, V>& x)
    {
        __pair_based_serial(x);
    }

    template<typename T>
    inline
    void process_serial_set(const std::set<T>& x)
    {
        __iterable_serial(x);
    }

    template<typename T>
    inline
    void process_serial_queue(const std::queue<T>& x)
    {
        size_t sz = x.size();
        _cont.append(reinterpret_cast<char*>(std::addressof(sz)), sizeof(sz));
        std::queue<T> _x(x);
        while (!_x.empty())
        {
            T item = std::move(_x.front());
            _x.pop();
            archive(item);
        }
    }

    template<typename T>
    inline
    void process_serial_stack(const std::stack<T>& x)
    {
        size_t sz = x.size();
        _cont.append(reinterpret_cast<char*>(std::addressof(sz)), sizeof(sz));
        std::stack<T> _x(x);
        while (!_x.empty())
        {
            T item = std::move(_x.top());
            _x.pop();
            archive(item);
        }
    }

    template<typename... Args>
    inline
    void process_serial_tuple(const std::tuple<Args...>& x)
    {
        __tuple_serial<std::tuple_size_v<std::tuple<Args...>>, Args...>(x);
    }

    template<typename T>
    inline
    typename std::enable_if_t<is_custom_type_v<rem_const_ref_t<T>>, void>
    process_serial_custom(T& x)
    {
        x.archive(*this);
    }

private:
    template<size_t Index, typename... Args>
    inline
    void __tuple_serial(const std::tuple<Args...>& _tp)
    {
        if constexpr (Index > 0) {
            __tuple_serial<Index - 1, Args...>(_tp);
            archive(std::get<Index - 1>(_tp));
        }
    }

    template<typename T>
    inline
    void __iterable_serial(const T& x)
    {
        size_t sz = x.size();
        _cont.append(reinterpret_cast<char*>(std::addressof(sz)), sizeof(sz));
        for (auto& item : x)
        {
            archive(item);
        }
    }

    template<typename T>
    inline
    void __pair_based_serial(const T& x)
    {
        size_t sz = x.size();
        _cont.append(reinterpret_cast<char*>(std::addressof(sz)), sizeof(sz));
        for (auto& [k, v] : x)
        {
            archive(k);
            archive(v);
        }
    }

private:
    std::string& _cont;
};

}


/**
 * 二进制反序列化工具
 */
namespace hello {

/**
 * 二进制反序列化工具
 */
class deserializer
{
public:
    explicit deserializer(std::string _s) : _cont(std::move(_s)), _pos(0) { }

public:
    template<typename... Args>
    inline
    void operator () (Args&&... args)
    {
        archive(std::forward<Args>(args)...);
    }

private:
    template<typename T, typename... Args>
    inline void archive(T&& first, Args&&... rest)
    {
        // 依据不同的类型匹配不同的反序列化函数
        using in_type = rem_const_ref_t<T>;
        if constexpr (is_builtin_type_v<in_type>)
            process_deserial_builtin(first);
        else if constexpr (is_stl_vector_v<in_type>)
            process_deserial_vector(first);
        else if constexpr (is_stl_string_v<in_type>)
            process_deserial_string(first);
        else if constexpr (is_stl_list_v<in_type>)
            process_deserial_list(first);
        else if constexpr (is_stl_array_v<in_type>)
            process_deserial_array(first);
        else if constexpr (is_stl_unordered_map_v<in_type>)
            process_deserial_unordered_map(first);
        else if constexpr (is_stl_unordered_set_v<in_type>)
            process_deserial_unordered_set(first);
        else if constexpr (is_stl_map_v<in_type>)
            process_deserial_map(first);
        else if constexpr (is_stl_set_v<in_type>)
            process_deserial_set(first);
        else if constexpr (is_stl_queue_v<in_type>)
            process_deserial_queue(first);
        else if constexpr (is_stl_stack_v<in_type>)
            process_deserial_stack(first);
        else if constexpr (is_stl_tuple_v<in_type>)
            process_deserial_tuple(first);
        else if constexpr (is_custom_type_v<in_type>)
            process_deserial_custom(first);
        else ;

        if constexpr (sizeof...(rest) > 0)
        {
            archive(rest...);
        }
    }

    template<typename T>
    inline
    typename std::enable_if<is_builtin_type<rem_const_ref_t<T>>::value, void>::type
    process_deserial_builtin(T& x)
    {
        char* _dt = _cont.data() + _pos;
        x = *((T*)_dt);
        _pos += sizeof(x);
    }

    template<typename T>
    inline
    void process_deserial_vector(std::vector<T>& x)
    {
        char* _dt = _cont.data() + _pos;
        size_t sz = *((size_t*)_dt);
        _pos += sizeof(sz);
        x.reserve(sz);
        for (auto i = 0; i < sz; ++i)
        {
            T item {};
            archive(item);
            x.emplace_back(std::move(item));
        }
    }

    inline void process_deserial_string(std::string& x)
    {
        char* _dt = _cont.data() + _pos;
        size_t sz = *((size_t*)_dt);
        _pos += sizeof(sz);
        x.append(_cont.data() + _pos, sz);
        _pos += sz;
    }

    template<typename T>
    inline
    void process_deserial_list(std::list<T>& x)
    {
        char* _dt = _cont.data() + _pos;
        size_t sz = *((size_t*)_dt);
        _pos += sizeof(sz);
        for (auto i = 0; i < sz; ++i)
        {
            T item {};
            archive(item);
            x.emplace_back(std::move(item));
        }
    }

    template<typename T, size_t N>
    inline
    void process_deserial_array(std::array<T, N>& x)
    {
        for (auto i = 0; i < N; ++i)
        {
            T item {};
            archive(item);
            x[i] = std::move(item);
        }
    }

    template<typename K, typename V>
    inline
    void process_deserial_unordered_map(std::unordered_map<K, V>& x)
    {
        __pair_based_deserial(x);
    }

    template<typename T>
    inline
    void process_deserial_unordered_set(std::unordered_set<T>& x)
    {
        __sz_emplace_deserial(x);
    }

    template<typename K, typename V>
    inline
    void process_deserial_map(std::map<K, V>& x)
    {
        __pair_based_deserial(x);
    }

    template<typename T>
    inline
    void process_deserial_set(std::set<T>& x)
    {
        __sz_emplace_deserial(x);
    }

    template<typename T>
    inline
    void process_deserial_queue(std::queue<T>& x)
    {
        __sz_emplace_deserial(x);
    }

    template<typename T>
    inline
    void process_deserial_stack(std::stack<T>& x)
    {
        char* _dt = _cont.data() + _pos;
        size_t sz = *((size_t*)_dt);
        _pos += sizeof(sz);
        std::stack<T> _x;
        for (auto i = 0; i < sz; ++i)
        {
            T item {};
            archive(item);
            _x.emplace(std::move(item));
        }
        while (!_x.empty())
        {
            x.emplace(std::move(_x.top()));
            _x.pop();
        }
    }

    template<typename... Args>
    inline
    void process_deserial_tuple(std::tuple<Args...>& x)
    {
        __tuple_deserial<std::tuple_size_v<std::tuple<Args...>>, Args...>(x);
    }

    template<typename T>
    inline
    typename std::enable_if<is_custom_type<rem_const_ref_t<T>>::value, void>::type
    process_deserial_custom(T& x)
    {
        x.archive(*this);
    }

private:
    template<size_t Index, typename... Args>
    inline
    void __tuple_deserial(std::tuple<Args...>& _tp)
    {
        if constexpr (Index > 0) {
            __tuple_deserial<Index - 1, Args...>(_tp);
            archive(std::get<Index - 1>(_tp));
        }
    }

    template<typename T>
    inline
    void __pair_based_deserial(T& x)
    {
        char* _dt = _cont.data() + _pos;
        size_t sz = *((size_t*)_dt);
        _pos += sizeof(sz);
        for (auto i = 0; i < sz; ++i)
        {
            typename T::key_type kitem {};
            archive(kitem);
            typename T::mapped_type vitem {};
            archive(vitem);
            x.emplace(std::move(kitem), std::move(vitem));
        }
    }

    template<typename T>
    inline
    void __sz_emplace_deserial(T& x)
    {
        char* _dt = _cont.data() + _pos;
        size_t sz = *((size_t*)_dt);
        _pos += sizeof(sz);
        for (auto i = 0; i < sz; ++i)
        {
            typename T::value_type item {};
            archive(item);
            x.emplace(std::move(item));
        }
    }

private:
    std::string _cont;
    size_t _pos;
};

}
